/*
 * Copyright 2008 The Microlog project @sourceforge.net
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.sf.microlog.midp.appender.s3;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;

import net.sf.microlog.core.Level;
import net.sf.microlog.midp.appender.FileAppender;

/**
 * This class logs to a file, just as the <code>FileAppender</code> does. When
 * triggered the file is stored on Amazon S3 ( http://aws.amazon.com/s3 ).
 * 
 * This requires MIDP 2.0 or greater with JSR-75 & an S3 library for Java ME
 * (J2ME). This is bundled with Microlog for your convenience.
 * 
 * The actual storing of the log is done by an instance of
 * <code>S3LogService</code>.
 * 
 * @author Johan Karlsson (johan.karlsson@jayway.se)
 */
public class S3FileAppender extends FileAppender {

	public static final int DEFAULT_READ_BUFFER_SIZE = 512;

	public static final int DEFAULT_TRIGGER_FILE_SIZE = 1024;

	/**
	 * This trigger disables the storing of the logfile to S3. When this is set,
	 * it works as an ordinary <code>FileAppender</code>.
	 */
	public final static int TRIGGER_DO_NOTHING = 0;

	public final static int TRIGGER_ON_FILE_SIZE = 1;

	public final static int TRIGGER_ON_LEVEL = 2;

	public static final String FILE_NAME_PROPERTY = "net.sf.microlog.appender.S3FileAppender.fileName";

	private int triggerSetting = TRIGGER_ON_LEVEL;

	/**
	 * This is the file size that we trigger on. It is expressed in bytes.
	 */
	private int triggerFileSize = DEFAULT_TRIGGER_FILE_SIZE;

	/**
	 * This is the trigger level that triggers the storing of the log.
	 */
	private Level triggerLevel = Level.ERROR;

	/**
	 * This is the log service that is used for storing the file on S3.
	 */
	private S3LogService logService;

	/**
	 * Create an <code>S3Appender</code> with default settings.
	 */
	public S3FileAppender() {
		logService = new S3SOAPLogService();
	}

	/**
	 * Set the access key ID for your Amazon Web Service (AWS) account.
	 * 
	 * @param accessKeyID
	 *            the access key id to use.
	 */
	public void setAccessKeyID(String accessKeyID) {
		logService.setAccessKeyID(accessKeyID);
	}

	/**
	 * Set the secret access key for your Amazon Web Service (AWS) account.
	 * 
	 * @param secretAccessKey
	 *            the access key to use.
	 */
	public void setSecretAccessKey(String secretAccessKey) {
		logService.setSecretAccessKey(secretAccessKey);
	}

	/**
	 * Get the trigger settings, which could one of the following
	 * <code>TRIGGER_DO_NOTHING</code>, <code>TRIGGER_ON_FILE_SIZE</code>
	 * or <code>TRIGGER_ON_LEVEL</code>.
	 * 
	 * @return the triggerSetting
	 */
	public synchronized int getTriggerSetting() {
		return triggerSetting;
	}

	/**
	 * Set the trigger settings, which could one of the following
	 * <code>TRIGGER_DO_NOTHING</code>, <code>TRIGGER_ON_FILE_SIZE</code>
	 * or <code>TRIGGER_ON_LEVEL</code>.
	 * 
	 * @param triggerSetting
	 *            the triggerSetting to set
	 * @throws IllegalArgumentException
	 *             if the triggerSetting is not a valid value.
	 */
	public synchronized void setTriggerSetting(int triggerSetting)
			throws IllegalArgumentException {
		if (triggerSetting < TRIGGER_DO_NOTHING
				|| triggerSetting > TRIGGER_ON_LEVEL) {
			throw new IllegalArgumentException(
					"The triggerSetting must be a value between 0..2");
		}

		this.triggerSetting = triggerSetting;
	}

	/**
	 * Get the file size (in bytes) which triggers a store on the server.
	 * 
	 * @return the triggerFileSize
	 */
	public int getTriggerFileSize() {
		return triggerFileSize;
	}

	/**
	 * Set the file size (in bytes) which triggers a store on the server.
	 * 
	 * @param triggerFileSize
	 *            the triggerFileSize to set
	 */
	public void setTriggerFileSize(int triggerFileSize) {
		this.triggerFileSize = triggerFileSize;
	}

	/**
	 * Get the level that is used for trigger a store on the server.
	 * 
	 * @return the triggerLevel
	 */
	public Level getTriggerLevel() {
		return triggerLevel;
	}

	/**
	 * Set the level that is used for trigger a store on the server.
	 * 
	 * @param triggerLevel
	 *            the triggerLevel to set
	 */
	public void setTriggerLevel(Level triggerLevel) {
		this.triggerLevel = triggerLevel;
	}

	/**
	 * This log to the file and stores it to the S3 server(s) if triggered.
	 * 
	 * @see net.sf.microlog.midp.appender.FileAppender#doLog(String,
	 *      String, long, net.sf.microlog.core.Level, java.lang.Object, java.lang.Throwable)
	 */
	public synchronized void doLog(String clientID, String name, long time,
			Level level, Object message, Throwable t) {
		super.doLog(clientID, name, time, level, message, t);

		boolean storeLog = false;

		if (triggerSetting == TRIGGER_DO_NOTHING) {
			storeLog = false;
		} else if (triggerSetting == TRIGGER_ON_FILE_SIZE
				&& getLogSize() > triggerFileSize) {
			storeLog = true;
		} else if (triggerSetting == TRIGGER_ON_LEVEL
				&& level.toInt() >= triggerLevel.toInt()) {
			storeLog = true;
		}

		if (storeLog) {
			final byte[] logData = getLogData();
			clear();
			new Thread(new Runnable() {
				public void run() {
					logService.storeLog(fileName, logData);
				}
			}).start();
		}

	}

	private synchronized byte[] getLogData() {
		// Get the byte array data from the file.
		byte[] readBuffer = new byte[DEFAULT_READ_BUFFER_SIZE];
		byte[] logData = null;
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		if (fileConnection != null) {
			try {
				DataInputStream dataInputStream = fileConnection
						.openDataInputStream();
				int bytesRead = dataInputStream.read(readBuffer);

				while (bytesRead != -1) {
					baos.write(readBuffer, 0, bytesRead);
					bytesRead = dataInputStream.read(readBuffer);
				}

				logData = baos.toByteArray();
			} catch (IOException e) {
				System.err.println("Failed to the the logdata " + e);
			}
		} else {
			System.err.println("No FileConnection available for reading data.");
		}

		return logData;
	}

	/**
	 * @see net.sf.microlog.core.Appender#configure(Properties)
	 */
	/*public void configure(Properties properties)
			throws IllegalArgumentException {
		if (properties == null) {
			throw new IllegalArgumentException(
					"The properties must not be null.");
		}

		super.configure(properties);

		String newFileName = properties.getProperty(FILE_NAME_PROPERTY);
		if (newFileName != null) {
			this.fileName = newFileName;
		}
	}*/
}
